package com.pt25.base.model.common.util.compress;

import com.pt25.base.util.JsonUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterOutputStream;

@Slf4j
@Service
public class FlaterCompressService {

    @Resource(name = "deflaterGenericObjectPool")
    private GenericObjectPool<Deflater> deflaterGenericObjectPool;

    @Resource(name = "inflaterGenericObjectPool")
    private GenericObjectPool<Inflater> inflaterGenericObjectPool;

    public byte[] compressByte(byte[] data, int bufferSize) throws Exception {
        Deflater deflater = deflaterGenericObjectPool.borrowObject();
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream, deflater, bufferSize)) {

            int offset = 0;
            // 防止死循环, 如果 input 数据损坏，inflater.finished() 可能永远不返回 true，导致 死循环
            int maxAttempts = 500;
            while (offset < data.length  && maxAttempts-- > 0) {
                int length = Math.min(bufferSize, data.length - offset);
                deflaterOutputStream.write(data, offset, length);
                offset += length;
            }
            if (maxAttempts <= 0) {
                throw new IllegalStateException("压缩失败，可能数据损坏!");
            }
            deflaterOutputStream.finish();
            return byteArrayOutputStream.toByteArray();

        } catch (IOException e) {
            throw new RuntimeException("Failed to compress data", e);
        } finally {
            deflater.reset();  // Ensure the deflater is reset before returning to the pool
            deflaterGenericObjectPool.returnObject(deflater);
        }
    }


    public byte[] uncompressByte(byte[] data, int bufferSize) throws Exception {
        Inflater inflater = inflaterGenericObjectPool.borrowObject();
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(byteArrayOutputStream, inflater, bufferSize)) {

            byte[] buffer = new byte[bufferSize];
            int length;
            // 防止死循环, 如果 input 数据损坏，inflater.finished() 可能永远不返回 true，导致 死循环
            int maxAttempts = 500;
            while ((length = byteArrayInputStream.read(buffer)) != -1 && maxAttempts-- > 0) {
                inflaterOutputStream.write(buffer, 0, length);
            }
            if (maxAttempts <= 0) {
                throw new IllegalStateException("解压失败，可能数据损坏!");
            }
            inflaterOutputStream.finish();
            return byteArrayOutputStream.toByteArray();

        } catch (IOException e) {
            throw new RuntimeException("Failed to uncompress data", e);
        } finally {
            inflater.reset();  // Ensure the inflater is reset before returning to the pool
            inflaterGenericObjectPool.returnObject(inflater);
        }
    }


    public String compress(Object data) {
        try {
            byte[] compressData = compressByte((JsonUtil.toJson(data)).getBytes(), 1024);
            return Base64.getEncoder().encodeToString(compressData);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public String compress(Object data, int size) {
        try {
            byte[] compressData = compressByte((JsonUtil.toJson(data)).getBytes(), size);
            return Base64.getEncoder().encodeToString(compressData);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public String uncompress(Object data) {
        try {
            String base64Data = (String) data;
            byte[] compressData = Base64.getDecoder().decode(base64Data);
            return new String(uncompressByte(compressData, 1024));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public String uncompress(Object data, int size) {
        try {
            String base64Data = (String) data;
            byte[] compressData = Base64.getDecoder().decode(base64Data);
            return new String(uncompressByte(compressData, size));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
